{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "f705f4be70e9"
      },
      "outputs": [],
      "source": [
        "# Copyright 2025 Google LLC\n",
        "#\n",
        "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "#     https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "6a63f50996b5"
      },
      "source": [
        "# Getting Started with Google A2A (Agent-to-Agent) Communication\n",
        "\n",
        "This notebook introduces you to Google's Agent-to-Agent (A2A) protocol, a standardized way for AI agents to communicate and collaborate.  "
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "d6581a815af6"
      },
      "source": [
        "<table align=\"left\">\n",
        "  <td style=\"text-align: center\">\n",
        "    <a href=\"https://colab.research.google.com/github/a2aproject/a2a-samples/blob/main/notebooks/a2a_quickstart.ipynb\">\n",
        "      <img width=\"32px\" src=\"https://www.gstatic.com/pantheon/images/bigquery/welcome_page/colab-logo.svg\" alt=\"Google Colaboratory logo\"><br> Open in Colab\n",
        "    </a>\n",
        "  </td>\n",
        "  <td style=\"text-align: center\">\n",
        "    <a href=\"https://console.cloud.google.com/vertex-ai/colab/import/https:%2F%2Fraw.githubusercontent.com%2Fa2aproject%2Fa2a-samples%2Fmain%2Fnotebooks%2Fa2a_quickstart.ipynb\">\n",
        "      <img width=\"32px\" src=\"https://lh3.googleusercontent.com/JmcxdQi-qOpctIvWKgPtrzZdJJK-J3sWE1RsfjZNwshCFgE_9fULcNpuXYTilIR2hjwN\" alt=\"Google Cloud Colab Enterprise logo\"><br> Open in Colab Enterprise\n",
        "    </a>\n",
        "  </td>\n",
        "  <td style=\"text-align: center\">\n",
        "    <a href=\"https://console.cloud.google.com/vertex-ai/workbench/deploy-notebook?download_url=https://raw.githubusercontent.com/a2aproject/a2a-samples/main/notebooks/a2a_quickstart.ipynb\">\n",
        "      <img src=\"https://www.gstatic.com/images/branding/gcpiconscolors/vertexai/v1/32px.svg\" alt=\"Vertex AI logo\"><br> Open in Vertex AI Workbench\n",
        "    </a>\n",
        "  </td>\n",
        "  <td style=\"text-align: center\">\n",
        "    <a href=\"https://console.cloud.google.com/bigquery/import?url=https://github.com/a2aproject/a2a-samples/blob/main/notebooks/a2a_quickstart.ipynb\">\n",
        "      <img src=\"https://www.gstatic.com/images/branding/gcpiconscolors/bigquery/v1/32px.svg\" alt=\"BigQuery Studio logo\"><br> Open in BigQuery Studio\n",
        "    </a>\n",
        "  </td>\n",
        "  <td style=\"text-align: center\">\n",
        "    <a href=\"https://github.com/a2aproject/a2a-samples/blob/main/notebooks/a2a_quickstart.ipynb\">\n",
        "      <img width=\"32px\" src=\"https://www.svgrepo.com/download/217753/github.svg\" alt=\"GitHub logo\"><br> View on GitHub\n",
        "    </a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "44f672f23c9c"
      },
      "source": [
        "## What You'll Build\n",
        "\n",
        "A three-agent system that works together to analyze trending topics:\n",
        "1. **Trending Topics Agent** - Searches the web for current trending topics\n",
        "2. **Trend Analyzer Agent** - Performs deep analysis with quantitative data\n",
        "3. **Host Agent** - Orchestrates the other agents to provide comprehensive insights"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "15d597d58ac1"
      },
      "source": [
        "<img src=\"https://storage.googleapis.com/github-repo/a2a/a2a-diagram.png\" alt=\"drawing\" width=\"1000\"/>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "f8bb6c6346eb"
      },
      "source": [
        "## Prerequisites\n",
        "\n",
        "- Python 3.11+\n",
        "- Google Cloud Project with Vertex AI enabled\n",
        "- Basic understanding of async Python\n",
        "\n",
        "## Other Resources\n",
        "\n",
        "- [Google ADK Documentation](https://google.github.io/adk-docs/)\n",
        "- [A2A Protocol Specification](https://github.com/google/a2a)\n",
        "- [Vertex AI Documentation](https://cloud.google.com/vertex-ai)\n",
        "- Codelabs:\n",
        "  - [Google's Agent Stack in Action: ADK, A2A, MCP on Google Cloud](https://codelabs.developers.google.com/instavibe-adk-multi-agents/instructions)\n",
        "  - [Getting Started with Agent-to-Agent (A2A) Protocol: Gemini on Cloud Run](https://codelabs.developers.google.com/intro-a2a-purchasing-concierge)\n",
        "  - [Getting Started with MCP, ADK and A2A](https://codelabs.developers.google.com/codelabs/currency-agent)\n",
        "\n",
        "#### Important!\n",
        "A2A is a work in progress (WIP) thus, in the near future there might be changes that are different from what demonstrated in this code."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "a689435e04b8"
      },
      "source": [
        "### Setup and Installation\n",
        "\n",
        "First, let's install the required dependencies:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "9b8e4fde0015"
      },
      "outputs": [],
      "source": [
        "# Install required packages\n",
        "%pip install --upgrade -q google-genai google-adk==1.9.0 a2a-sdk==0.3.0 python-dotenv aiohttp uvicorn requests mermaid-python nest-asyncio"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "415604558ae1"
      },
      "source": [
        "## 1. Introduction to A2A\n",
        "\n",
        "### What is Agent-to-Agent (A2A) Communication?\n",
        "\n",
        "A2A is a standardized protocol that enables AI agents to:\n",
        "- **Discover** each other's capabilities\n",
        "- **Communicate** using a common JSON-RPC based protocol\n",
        "- **Collaborate** to solve complex tasks\n",
        "- **Stream** responses for real-time interactions"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8991eb966e73"
      },
      "source": [
        "### Environment Configuration"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "bba60fb0f91a"
      },
      "outputs": [],
      "source": [
        "# Targeted workaround for google-adk==1.9.0 compatibility with a2a-sdk==0.3.0\n",
        "# This cell shall be removed when google-adk releases the version next to >1.9.0\n",
        "# (after https://github.com/google/adk-python/pull/2297)\n",
        "\n",
        "\n",
        "import sys\n",
        "\n",
        "from a2a.client import client as real_client_module\n",
        "from a2a.client.card_resolver import A2ACardResolver\n",
        "\n",
        "\n",
        "class PatchedClientModule:\n",
        "    def __init__(self, real_module) -> None:\n",
        "        for attr in dir(real_module):\n",
        "            if not attr.startswith('_'):\n",
        "                setattr(self, attr, getattr(real_module, attr))\n",
        "        self.A2ACardResolver = A2ACardResolver\n",
        "\n",
        "\n",
        "patched_module = PatchedClientModule(real_client_module)\n",
        "sys.modules['a2a.client.client'] = patched_module  # type: ignore"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "db7e4734f7f3"
      },
      "outputs": [],
      "source": [
        "import asyncio\n",
        "import logging\n",
        "import os\n",
        "import sys\n",
        "import threading\n",
        "import time\n",
        "\n",
        "from typing import Any\n",
        "\n",
        "import httpx\n",
        "import nest_asyncio\n",
        "import uvicorn\n",
        "\n",
        "from a2a.client import ClientConfig, ClientFactory, create_text_message_object\n",
        "from a2a.server.apps import A2AStarletteApplication\n",
        "from a2a.server.request_handlers import DefaultRequestHandler\n",
        "from a2a.server.tasks import InMemoryTaskStore\n",
        "from a2a.types import (\n",
        "    AgentCapabilities,\n",
        "    AgentCard,\n",
        "    AgentSkill,\n",
        "    TransportProtocol,\n",
        ")\n",
        "from a2a.utils.constants import AGENT_CARD_WELL_KNOWN_PATH\n",
        "from dotenv import load_dotenv\n",
        "from google.adk.a2a.executor.a2a_agent_executor import (\n",
        "    A2aAgentExecutor,\n",
        "    A2aAgentExecutorConfig,\n",
        ")\n",
        "from google.adk.agents import Agent, SequentialAgent\n",
        "from google.adk.agents.remote_a2a_agent import RemoteA2aAgent\n",
        "from google.adk.artifacts import InMemoryArtifactService\n",
        "from google.adk.memory.in_memory_memory_service import InMemoryMemoryService\n",
        "from google.adk.runners import Runner\n",
        "from google.adk.sessions import InMemorySessionService\n",
        "from google.adk.tools import google_search"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "2c229a99c6b8"
      },
      "outputs": [],
      "source": [
        "# Set Google Cloud Configuration\n",
        "os.environ['GOOGLE_GENAI_USE_VERTEXAI'] = 'TRUE'\n",
        "os.environ['GOOGLE_CLOUD_PROJECT'] = (\n",
        "    '[your-project-id]'  # @param {type: \"string\", placeholder: \"[your-project-id]\", isTemplate: true}\n",
        ")\n",
        "os.environ['GOOGLE_CLOUD_LOCATION'] = (\n",
        "    'us-central1'  # Replace with your location\n",
        ")\n",
        "\n",
        "load_dotenv()\n",
        "\n",
        "print('Environment variables configured:')\n",
        "print(f'GOOGLE_GENAI_USE_VERTEXAI: {os.environ[\"GOOGLE_GENAI_USE_VERTEXAI\"]}')\n",
        "print(f'GOOGLE_CLOUD_PROJECT: {os.environ[\"GOOGLE_CLOUD_PROJECT\"]}')\n",
        "print(f'GOOGLE_CLOUD_LOCATION: {os.environ[\"GOOGLE_CLOUD_LOCATION\"]}')"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "8ed891e4daf5"
      },
      "outputs": [],
      "source": [
        "# Authenticate your notebook environment (Colab only)\n",
        "if 'google.colab' in sys.modules:\n",
        "    from google.colab import auth\n",
        "\n",
        "    auth.authenticate_user(project_id=os.environ['GOOGLE_CLOUD_PROJECT'])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "f3fbb50b64fd"
      },
      "outputs": [],
      "source": [
        "# Setup logging\n",
        "logging.basicConfig(\n",
        "    level=logging.ERROR,\n",
        "    format='%(asctime)s - %(levelname)s - %(name)s - %(message)s',\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "bf7079208e95"
      },
      "source": [
        "## 2. Building Your A2A System\n",
        "\n",
        "Let's build our three-agent system step by step. We'll create:\n",
        "\n",
        "1. **Trending Topics Agent** - Finds current trending topics\n",
        "2. **Trend Analyzer Agent** - Analyzes trends with quantitative data\n",
        "3. **Host Agent** - Orchestrates the other agents (sequentially)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "e1d2442d987d"
      },
      "source": [
        "### Agent 1: Trending Topics Agent\n",
        "\n",
        "This agent searches the web for trending topics and returns a list of current trends."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "001caeaf844f"
      },
      "outputs": [],
      "source": [
        "# Create the Trending Topics ADK Agent\n",
        "trending_agent = Agent(\n",
        "    model='gemini-2.5-pro',\n",
        "    name='trending_topics_agent',\n",
        "    instruction=\"\"\"\n",
        "    You are a social media trends analyst. Your job is to search the web for current trending topics,\n",
        "    particularly from social platforms.\n",
        "\n",
        "    When asked about trends:\n",
        "    1. Search for \"trending topics today\" or similar queries\n",
        "    2. Extract the top 3 trending topics\n",
        "    3. Return them in a JSON format\n",
        "\n",
        "    Focus on current, real-time trends from the last 24 hours.\n",
        "\n",
        "    You MUST return your response in the following JSON format:\n",
        "    {\n",
        "        \"trends\": [\n",
        "            {\n",
        "                \"topic\": \"Topic name\",\n",
        "                \"description\": \"Brief description (1-2 sentences)\",\n",
        "                \"reason\": \"Why it's trending\"\n",
        "            },\n",
        "            {\n",
        "                \"topic\": \"Topic name\",\n",
        "                \"description\": \"Brief description (1-2 sentences)\",\n",
        "                \"reason\": \"Why it's trending\"\n",
        "            },\n",
        "            {\n",
        "                \"topic\": \"Topic name\",\n",
        "                \"description\": \"Brief description (1-2 sentences)\",\n",
        "                \"reason\": \"Why it's trending\"\n",
        "            }\n",
        "        ]\n",
        "    }\n",
        "\n",
        "    Only return the JSON object, no additional text.\n",
        "    \"\"\",\n",
        "    tools=[google_search],\n",
        ")\n",
        "\n",
        "print('Trending Topics Agent created successfully!')"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "07acb61dae80"
      },
      "outputs": [],
      "source": [
        "trending_agent_card = AgentCard(\n",
        "    name='Trending Topics Agent',\n",
        "    url='http://localhost:10020',\n",
        "    description='Searches the web for current trending topics from social media',\n",
        "    version='1.0',\n",
        "    capabilities=AgentCapabilities(streaming=True),\n",
        "    default_input_modes=['text/plain'],\n",
        "    default_output_modes=['text/plain'],\n",
        "    preferred_transport=TransportProtocol.jsonrpc,\n",
        "    skills=[\n",
        "        AgentSkill(\n",
        "            id='find_trends',\n",
        "            name='Find Trending Topics',\n",
        "            description='Searches for current trending topics on social media',\n",
        "            tags=['trends', 'social media', 'twitter', 'current events'],\n",
        "            examples=[\n",
        "                \"What's trending today?\",\n",
        "                'Show me current Twitter trends',\n",
        "                'What are people talking about on social media?',\n",
        "            ],\n",
        "        )\n",
        "    ],\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "db664b4c9e01"
      },
      "outputs": [],
      "source": [
        "remote_trending_agent = RemoteA2aAgent(\n",
        "    name='find_trends',\n",
        "    description='Searches for current trending topics on social media',\n",
        "    agent_card=f'http://localhost:10020{AGENT_CARD_WELL_KNOWN_PATH}',\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "f14d4d0b57a4"
      },
      "source": [
        "### Agent 2: Trend Analyzer Agent\n",
        "\n",
        "This agent takes a specific trend and performs deep analysis with quantitative data."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "161a348d7104"
      },
      "outputs": [],
      "source": [
        "# Create the Trend Analyzer ADK Agent\n",
        "analyzer_agent = Agent(\n",
        "    model='gemini-2.5-pro',\n",
        "    name='trend_analyzer_agent',\n",
        "    instruction=\"\"\"\n",
        "    You are a data analyst specializing in trend analysis. When given a trending topic,\n",
        "    perform deep research to find quantitative data and insights.\n",
        "\n",
        "    For each trend you analyze:\n",
        "    1. Search for statistics, numbers, and metrics related to the trend\n",
        "    2. Look for:\n",
        "       - Engagement metrics (views, shares, mentions)\n",
        "       - Growth rates and timeline\n",
        "       - Geographic distribution\n",
        "       - Related hashtags or keywords\n",
        "    3. Provide concrete numbers and data points\n",
        "\n",
        "    Keep it somehow concise\n",
        "\n",
        "    Always prioritize quantitative information over qualitative descriptions.\n",
        "    \"\"\",\n",
        "    tools=[google_search],\n",
        ")\n",
        "\n",
        "print('Trend Analyzer Agent created successfully!')"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "77776a1d9ad8"
      },
      "outputs": [],
      "source": [
        "analyzer_agent_card = AgentCard(\n",
        "    name='Trend Analyzer Agent',\n",
        "    url='http://localhost:10021',\n",
        "    description='Performs deep analysis of trends with quantitative data',\n",
        "    version='1.0',\n",
        "    capabilities=AgentCapabilities(streaming=True),\n",
        "    default_input_modes=['text/plain'],\n",
        "    default_output_modes=['text/plain'],\n",
        "    preferred_transport=TransportProtocol.jsonrpc,\n",
        "    skills=[\n",
        "        AgentSkill(\n",
        "            id='analyze_trend',\n",
        "            name='Analyze Trend',\n",
        "            description='Provides quantitative analysis of a specific trend',\n",
        "            tags=['analysis', 'data', 'metrics', 'statistics'],\n",
        "            examples=[\n",
        "                'Analyze the #ClimateChange trend',\n",
        "                'Get metrics for the Taylor Swift trend',\n",
        "                'Provide data analysis for AI adoption trend',\n",
        "            ],\n",
        "        )\n",
        "    ],\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "16195264f4a6"
      },
      "outputs": [],
      "source": [
        "remote_analyzer_agent = RemoteA2aAgent(\n",
        "    name='analyze_trend',\n",
        "    description='Provides quantitative analysis of a specific trend',\n",
        "    agent_card=f'http://localhost:10021{AGENT_CARD_WELL_KNOWN_PATH}',\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "18e76145ebd4"
      },
      "source": [
        "### Agent 3: Host Agent (Orchestrator)\n",
        "\n",
        "The Host Agent coordinates between the other two agents to provide comprehensive trend analysis."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "99522ad83421"
      },
      "outputs": [],
      "source": [
        "# Create the Host ADK Agent\n",
        "host_agent = SequentialAgent(\n",
        "    name='trend_analysis_host',\n",
        "    sub_agents=[remote_trending_agent, remote_analyzer_agent],\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "73923b4a4a0e"
      },
      "outputs": [],
      "source": [
        "host_agent_card = AgentCard(\n",
        "    name='Trend Analysis Host',\n",
        "    url='http://localhost:10022',\n",
        "    description='Orchestrates, sequentially, trend discovery and analysis using specialized agents',\n",
        "    version='1.0',\n",
        "    capabilities=AgentCapabilities(streaming=True),\n",
        "    default_input_modes=['text/plain'],\n",
        "    default_output_modes=['application/json'],\n",
        "    preferred_transport=TransportProtocol.jsonrpc,\n",
        "    skills=[\n",
        "        AgentSkill(\n",
        "            id='comprehensive_trend_analysis',\n",
        "            name='Comprehensive Trend Analysis',\n",
        "            description='Finds trending topics and provides deep analysis of the most relevant one',\n",
        "            tags=['trends', 'analysis', 'orchestration', 'insights'],\n",
        "            examples=[\n",
        "                'Analyze current trends',\n",
        "                \"What's trending and why is it important?\",\n",
        "                'Give me a comprehensive trend report',\n",
        "            ],\n",
        "        )\n",
        "    ],\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1802fda73322"
      },
      "source": [
        "## 3. Running\n",
        "\n",
        "Now let's put everything together. We'll create helper functions to start our agents and run the complete system."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "5b4c7a75c7ec"
      },
      "source": [
        "### Starting the A2A Servers\n",
        "\n",
        "Create function to run each agent as an A2A server:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "60b96fd4e891"
      },
      "outputs": [],
      "source": [
        "def create_agent_a2a_server(agent, agent_card):\n",
        "    \"\"\"Create an A2A server for any ADK agent.\n",
        "\n",
        "    Args:\n",
        "        agent: The ADK agent instance\n",
        "        agent_card: The ADK agent card\n",
        "\n",
        "    Returns:\n",
        "        A2AStarletteApplication instance\n",
        "    \"\"\"\n",
        "    runner = Runner(\n",
        "        app_name=agent.name,\n",
        "        agent=agent,\n",
        "        artifact_service=InMemoryArtifactService(),\n",
        "        session_service=InMemorySessionService(),\n",
        "        memory_service=InMemoryMemoryService(),\n",
        "    )\n",
        "\n",
        "    config = A2aAgentExecutorConfig()\n",
        "    executor = A2aAgentExecutor(runner=runner, config=config)\n",
        "\n",
        "    request_handler = DefaultRequestHandler(\n",
        "        agent_executor=executor,\n",
        "        task_store=InMemoryTaskStore(),\n",
        "    )\n",
        "\n",
        "    # Create A2A application\n",
        "    return A2AStarletteApplication(\n",
        "        agent_card=agent_card, http_handler=request_handler\n",
        "    )"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "576bfe3aee3e"
      },
      "outputs": [],
      "source": [
        "# Apply nest_asyncio\n",
        "nest_asyncio.apply()\n",
        "\n",
        "# Store server tasks\n",
        "server_tasks: list[asyncio.Task] = []\n",
        "\n",
        "\n",
        "async def run_agent_server(agent, agent_card, port) -> None:\n",
        "    \"\"\"Run a single agent server.\"\"\"\n",
        "    app = create_agent_a2a_server(agent, agent_card)\n",
        "\n",
        "    config = uvicorn.Config(\n",
        "        app.build(),\n",
        "        host='127.0.0.1',\n",
        "        port=port,\n",
        "        log_level='warning',\n",
        "        loop='none',  # Important: let uvicorn use the current loop\n",
        "    )\n",
        "\n",
        "    server = uvicorn.Server(config)\n",
        "    await server.serve()\n",
        "\n",
        "\n",
        "async def start_all_servers() -> None:\n",
        "    \"\"\"Start all servers in the same event loop.\"\"\"\n",
        "    # Create tasks for all servers\n",
        "    tasks = [\n",
        "        asyncio.create_task(\n",
        "            run_agent_server(trending_agent, trending_agent_card, 10020)\n",
        "        ),\n",
        "        asyncio.create_task(\n",
        "            run_agent_server(analyzer_agent, analyzer_agent_card, 10021)\n",
        "        ),\n",
        "        asyncio.create_task(\n",
        "            run_agent_server(host_agent, host_agent_card, 10022)\n",
        "        ),\n",
        "    ]\n",
        "\n",
        "    # Give servers time to start\n",
        "    await asyncio.sleep(2)\n",
        "\n",
        "    print('✅ All agent servers started!')\n",
        "    print('   - Trending Agent: http://127.0.0.1:10020')\n",
        "    print('   - Analyzer Agent: http://127.0.0.1:10021')\n",
        "    print('   - Host Agent: http://127.0.0.1:10022')\n",
        "\n",
        "    # Keep servers running\n",
        "    try:\n",
        "        await asyncio.gather(*tasks)\n",
        "    except KeyboardInterrupt:\n",
        "        print('Shutting down servers...')\n",
        "\n",
        "\n",
        "# Run in a background thread\n",
        "\n",
        "\n",
        "def run_servers_in_background() -> None:\n",
        "    loop = asyncio.new_event_loop()\n",
        "    asyncio.set_event_loop(loop)\n",
        "    loop.run_until_complete(start_all_servers())\n",
        "\n",
        "\n",
        "# Start the thread\n",
        "server_thread = threading.Thread(target=run_servers_in_background, daemon=True)\n",
        "server_thread.start()\n",
        "\n",
        "# Wait for servers to be ready\n",
        "time.sleep(3)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8211c3344a60"
      },
      "source": [
        "## 4. Testing the System\n",
        "\n",
        "### Call the A2A agents (the 2 remote agents, and the host agent that refers to the 2 remote agents as sub agents)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "03e7b1c16c1a"
      },
      "outputs": [],
      "source": [
        "class A2ASimpleClient:\n",
        "    \"\"\"A2A Simple to call A2A servers.\"\"\"\n",
        "\n",
        "    def __init__(self, default_timeout: float = 240.0):\n",
        "        self._agent_info_cache: dict[\n",
        "            str, dict[str, Any] | None\n",
        "        ] = {}  # Cache for agent metadata\n",
        "        self.default_timeout = default_timeout\n",
        "\n",
        "    async def create_task(self, agent_url: str, message: str) -> str:\n",
        "        \"\"\"Send a message following the official A2A SDK pattern.\"\"\"\n",
        "        # Configure httpx client with timeout\n",
        "        timeout_config = httpx.Timeout(\n",
        "            timeout=self.default_timeout,\n",
        "            connect=10.0,\n",
        "            read=self.default_timeout,\n",
        "            write=10.0,\n",
        "            pool=5.0,\n",
        "        )\n",
        "\n",
        "        async with httpx.AsyncClient(timeout=timeout_config) as httpx_client:\n",
        "            # Check if we have cached agent card data\n",
        "            if (\n",
        "                agent_url in self._agent_info_cache\n",
        "                and self._agent_info_cache[agent_url] is not None\n",
        "            ):\n",
        "                agent_card_data = self._agent_info_cache[agent_url]\n",
        "            else:\n",
        "                # Fetch the agent card\n",
        "                agent_card_response = await httpx_client.get(\n",
        "                    f'{agent_url}{AGENT_CARD_WELL_KNOWN_PATH}'\n",
        "                )\n",
        "                agent_card_data = self._agent_info_cache[agent_url] = (\n",
        "                    agent_card_response.json()\n",
        "                )\n",
        "\n",
        "            # Create AgentCard from data\n",
        "            agent_card = AgentCard(**agent_card_data)\n",
        "\n",
        "            # Create A2A client with the agent card\n",
        "            config = ClientConfig(\n",
        "                httpx_client=httpx_client,\n",
        "                supported_transports=[\n",
        "                    TransportProtocol.jsonrpc,\n",
        "                    TransportProtocol.http_json,\n",
        "                ],\n",
        "                use_client_preference=True,\n",
        "            )\n",
        "\n",
        "            factory = ClientFactory(config)\n",
        "            client = factory.create(agent_card)\n",
        "\n",
        "            # Create the message object\n",
        "            message_obj = create_text_message_object(content=message)\n",
        "\n",
        "            # Send the message and collect responses\n",
        "            responses = []\n",
        "            async for response in client.send_message(message_obj):\n",
        "                responses.append(response)\n",
        "\n",
        "            # The response is a tuple - get the first element (Task object)\n",
        "            if (\n",
        "                responses\n",
        "                and isinstance(responses[0], tuple)\n",
        "                and len(responses[0]) > 0\n",
        "            ):\n",
        "                task = responses[0][0]  # First element of the tuple\n",
        "\n",
        "                # Extract text: task.artifacts[0].parts[0].root.text\n",
        "                try:\n",
        "                    return task.artifacts[0].parts[0].root.text\n",
        "                except (AttributeError, IndexError):\n",
        "                    return str(task)\n",
        "\n",
        "            return 'No response received'"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "c5dbc3d5a442"
      },
      "outputs": [],
      "source": [
        "a2a_client = A2ASimpleClient()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "a8a1d8d5055d"
      },
      "outputs": [],
      "source": [
        "async def test_trending_topics() -> None:\n",
        "    \"\"\"Test trending topics agent.\"\"\"\n",
        "    trending_topics = await a2a_client.create_task(\n",
        "        'http://localhost:10020', \"What's trending today?\"\n",
        "    )\n",
        "    print(trending_topics)\n",
        "\n",
        "\n",
        "# Run the async function\n",
        "asyncio.run(test_trending_topics())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "997a99f1b9ab"
      },
      "outputs": [],
      "source": [
        "async def test_analysis() -> None:\n",
        "    \"\"\"Test analysis agent.\"\"\"\n",
        "    analysis = await a2a_client.create_task(\n",
        "        'http://localhost:10021', 'Analyze the trend AI in Social Media'\n",
        "    )\n",
        "    print(analysis)\n",
        "\n",
        "\n",
        "# Run the async function\n",
        "asyncio.run(test_analysis())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "524a44a8cb18"
      },
      "outputs": [],
      "source": [
        "async def test_host_analysis() -> None:\n",
        "    \"\"\"Test host analysis agent.\"\"\"\n",
        "    host_analysis = await a2a_client.create_task(\n",
        "        'http://localhost:10022',\n",
        "        'Find the most relevant trends in the web today, choose randomly one of the top '\n",
        "        'trends, and give me a complete analysis of it with quantitative data',\n",
        "    )\n",
        "    print(host_analysis)\n",
        "\n",
        "\n",
        "# Run the async function\n",
        "asyncio.run(test_host_analysis())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dc0dcc1fdce1"
      },
      "source": [
        "## Summary\n",
        "\n",
        "Congratulations! You've successfully built a multi-agent system using Google's A2A protocol. Here's what you've learned:\n",
        "\n",
        "1. **A2A Protocol Basics**: How agents discover and communicate with each other\n",
        "2. **ADK Integration**: Creating ADK agents and wrapping them for A2A\n",
        "3. **Agent Orchestration**: Building a Host Agent that coordinates multiple agents\n",
        "4. **Practical Implementation**: Running and testing a complete multi-agent system\n",
        "\n",
        "### Next Steps\n",
        "\n",
        "- **Deploy Your Agents**: Deploy agents to Cloud Run or other platforms\n",
        "- **Add Authentication**: Implement security for production use\n",
        "- **Create More Agents**: Build agents for your specific use cases, even using other frameworks\n",
        "- **Advanced Patterns**: Explore agent chains, parallel execution, and more\n",
        "- **Callbacks**: Add in the Google ADK agents the before and after callbacks of the agent, model and tool, to increase observability\n",
        "\n",
        "Happy agent building! 🚀"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "13f0c7f08d25"
      },
      "source": [
        "# Appendix\n",
        "\n",
        "### Why Use Google A2A (Agent-to-Agent) Protocol\n",
        "\n",
        "Google's Agent-to-Agent (A2A) protocol is a standardized communication framework that enables AI agents to discover, communicate, and collaborate with each other using a common JSON-RPC based protocol.  \n",
        "It provides a uniform way for agents to interact, regardless of their underlying implementation.  \n",
        "\n",
        "#### 1. Standardized Communication Protocol\n",
        "\n",
        "- A2A provides a consistent, JSON-RPC based protocol that any agent can implement\n",
        "- Agents can communicate without needing to know each other's internal implementation details\n",
        "- The protocol supports streaming responses for real-time interactions\n",
        "\n",
        "#### 2. Agent Discovery and Metadata\n",
        "\n",
        "- Agents expose their capabilities through standardized metadata (AgentCard)\n",
        "- Each agent publishes its skills, input/output modes, and capabilities\n",
        "- Host agents can dynamically discover what other agents can do through the `.well-known/agent-card.json` endpoint\n",
        "\n",
        "#### 3. Orchestration and Composition\n",
        "\n",
        "- Enables building complex multi-agent systems where a host agent can orchestrate multiple specialized agents\n",
        "- Supports sequential and parallel task execution patterns\n",
        "- Allows for sophisticated agent collaboration workflows\n",
        "\n",
        "#### 4. Platform Independence\n",
        "\n",
        "- A2A servers can wrap agents from different frameworks (not just ADK)\n",
        "- Agents can be deployed as independent services on different infrastructure\n",
        "- Promotes loose coupling between agents\n",
        "\n",
        "### Differences: Using ADK Agents Directly vs. Through A2A\n",
        "\n",
        "#### Using ADK Agents Directly\n",
        "\n",
        "```python\n",
        "# Conceptual Example: Defining Hierarchy\n",
        "from google.adk.agents import LlmAgent, BaseAgent\n",
        "\n",
        "# Define individual agents\n",
        "greeter = LlmAgent(name=\"Greeter\", model=\"gemini-2.5-pro\")\n",
        "task_doer = BaseAgent(name=\"TaskExecutor\") # Custom non-LLM agent\n",
        "\n",
        "# Create parent agent and assign children via sub_agents\n",
        "coordinator = LlmAgent(\n",
        "    name=\"Coordinator\",\n",
        "    model=\"gemini-2.5-pro\",\n",
        "    description=\"I coordinate greetings and tasks.\",\n",
        "    sub_agents=[ # Assign sub_agents here\n",
        "        greeter,\n",
        "        task_doer\n",
        "    ]\n",
        ")\n",
        "```\n",
        "\n",
        "__Use Direct ADK for Multi-Agents System When:__\n",
        "\n",
        "- All agents are tightly related and always used together\n",
        "- Google ADK is the framework choice, and simplicity is prioritized\n",
        "- Performance of in-process communication is critical\n",
        "- You don't need distributed deployment\n",
        "- No built-in service discovery is needed\n",
        "\n",
        "#### Using ADK Agents Through A2A\n",
        "\n",
        "__Use A2A for Multi-Agents System When:__\n",
        "\n",
        "- Building complex multi-agent systems\n",
        "- Agents need to be developed, deployed, and scaled independently\n",
        "- You want to integrate agents from different teams or frameworks\n",
        "- You need dynamic agent discovery and composition\n",
        "- Building a platform where agents can be added/removed dynamically\n",
        "- You want to enable third-party agent integration"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "name": "a2a_quickstart.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": ".venv",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "name": "python",
      "version": "3.13.3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
